package api

import (
	"strings"
	"strconv"
	"reflect"
	"math/big"
	"unicode/utf8"
	"encoding/json"
	"kumachan/standalone/ctn"
	"kumachan/standalone/util"
	"kumachan/interpreter/runtime/rt"
)


type Encoding  int
const (
	UTF8  Encoding  =  iota
)
func StringDecodeCasual(bytes ([] byte), e Encoding) string {
	switch e {
	case UTF8:
		var buf strings.Builder
		for len(bytes) > 0 {
			var char, size = utf8.DecodeRune(bytes)
			buf.WriteRune(char)
			bytes = bytes[size:]
		}
		return buf.String()
	default:
		panic("unknown or unimplemented encoding")
	}
}
func StringDecode(bytes ([] byte), e Encoding) (string, bool) {
	switch e {
	case UTF8:
		var buf strings.Builder
		for len(bytes) > 0 {
			var char, size = utf8.DecodeRune(bytes)
			if char == utf8.RuneError && size == 1 {
				// Note: An error should be thrown when input is invalid
				//       to ensure this function to be invertible.
				return "", false
			}
			buf.WriteRune(char)
			bytes = bytes[size:]
		}
		return buf.String(), true
	default:
		panic("unknown or unimplemented encoding")
	}
}
func StringEncode(str string, e Encoding) ([] byte) {
	switch e {
	case UTF8:
		return ([] byte)(str)
	default:
		panic("unknown or unimplemented encoding")
	}
}
func StringCompare(a string, b string) ctn.Ordering {
	return ctn.StringCompare(a, b)
}
func StringConcat(l rt.List) string {
	var buf strings.Builder
	l.ForEach(func(i uint, item rt.Value) {
		buf.WriteString(item.(string))
	})
	return buf.String()
}
func StringHasSubstring(operand string, sub string) bool {
	return (strings.Index(operand, sub) != -1)
}
type StringIterator struct {
	Operand  string
}
func (it *StringIterator) GetItemType() reflect.Type {
	return reflect.TypeOf(rune(0))
}
func (it *StringIterator) Next() (rt.Value, rt.Seq, bool) {
	if it == nil || it.Operand == "" {
		return nil, nil, false
	}
	var op = it.Operand
	for _, char := range op {
		var rest = op[utf8.RuneLen(char):]
		return char, &StringIterator { rest }, true
	}
	panic("impossible branch")
}
func StringSplit(operand string, sep string) rt.Seq {
	return rt.ListFrom(strings.Split(operand, sep)).Iterate()
}
func StringJoin(seq rt.Seq, sep string) string {
	var buf strings.Builder
	var index = uint(0)
	for v,rest,ok := seq.Next(); ok; v,rest,ok = rest.Next() {
		if index > 0 {
			buf.WriteString(sep)
		}
		buf.WriteString(v.(string))
		index += 1
	}
	return buf.String()
}

func __CharEqual(a rune, b rune) rt.EnumValue {
	return rt.ToBool(a == b)
}
func __CharFromInt(n *big.Int) rt.EnumValue {
	var char, ok = (func() (rune, bool) {
		if !(n.IsUint64()) {
			return -1, false
		}
		var n = n.Uint64()
		if n <= 0x10FFFF && !(0xD800 <= n && n <= 0xDFFF) {
			return rune(n), true
		} else {
			return -1, false
		}
	})()
	if ok {
		return rt.Some(char)
	} else {
		return rt.None()
	}
}

func __StringFromChar(char rune) string {
	return string([] rune { char })
}
func __StringFromListOfChar(v rt.Value) rt.Value {
	var arr = rt.ListFrom(v)
	return arr.CopyAsString()
}
func __StringEqual(a string, b string) rt.EnumValue {
	return rt.ToBool(a == b)
}
func __StringLessThan(a string, b string) rt.EnumValue {
	return rt.ToBool(a < b)
}
func __StringCompare(a string, b string) rt.EnumValue {
	return rt.ToOrdering(StringCompare(a, b))
}
func __StringToSeq(s string) rt.Seq {
	return &StringIterator { Operand: s }
}

func __EncodeUtf8(str string) ([] byte) {
	return StringEncode(str, UTF8)
}
func __DecodeUtf8(bytes ([] byte)) rt.EnumValue {
	var str, ok = StringDecode(bytes, UTF8)
	if ok {
		return rt.Some(str)
	} else {
		return rt.None()
	}
}
func __DecodeUtf8Casual(bytes ([] byte)) string {
	return StringDecodeCasual(bytes, UTF8)
}

func __Quote(str string) string {
	var buf = make([] rune, 0, len(str)+2)
	buf = append(buf, '"')
	for _, char := range str {
		switch char {
		case '\\', '"':
			buf = append(buf, '\\')
			buf = append(buf, char)
		case '\n':
			buf = append(buf, '\\', 'n')
		case '\r':
			buf = append(buf, '\\', 'r')
		case '\t':
			buf = append(buf, '\\', 't')
		default:
			if strconv.IsPrint(char) {
				buf = append(buf, char)
			} else {
				var bin, err = json.Marshal(string([] rune { char }))
				if err != nil { panic("something went wrong") }
				var escaped = ([] rune)(string(bin[1:len(bin)-1]))
				buf = append(buf, escaped...)
			}
		}
	}
	buf = append(buf, '"')
	return string(buf)
}
func __Unquote(s string) rt.EnumValue {
	var buf strings.Builder
	if !(len(s) >= 2 && s[0] == '"' && s[len(s)-1] == '"') {
		return rt.None()
	}
	s = s[1:len(s)-1]
	for len(s) > 0 {
		var r, _, rest, err = strconv.UnquoteChar(s, byte('"'))
		if err != nil {
			return rt.None()
		}
		buf.WriteRune(r)
		s = rest
	}
	var unquoted = buf.String()
	return rt.Some(unquoted)
}

func __StringConcat(v rt.Value) string {
	return StringConcat(rt.ListFrom(v))
}

func __StringUtf8Length(str string) *big.Int {
	return util.GetNumberUint(uint(len(str)))
}

func __StringShift(str string) rt.EnumValue {
	if len(str) > 0 {
		for _, char := range str {
			var rest = str[utf8.RuneLen(char):]
			return rt.Some(rt.Tuple(char, rest))
		}
		panic("impossible branch")
	} else {
		return rt.None()
	}
}
func __StringShiftPrefix(str string, prefix string) rt.EnumValue {
	if strings.HasPrefix(str, prefix) {
		return rt.Some(str[len(prefix):])
	} else {
		return rt.None()
	}
}

func __StringSplit(operand string, sep string) rt.Seq {
	return StringSplit(operand, sep)
}
func __StringJoin(seq rt.Seq, sep string) string {
	return StringJoin(seq, sep)
}

func __StringTrim(operand string, chars string) string {
	return strings.Trim(operand, chars)
}
func __StringTrimLeft(operand string, chars string) string {
	return strings.TrimLeft(operand, chars)
}
func __StringTrimRight(operand string, chars string) string {
	return strings.TrimRight(operand, chars)
}
func __StringTrimPrefix(operand string, prefix string) string {
	return strings.TrimPrefix(operand, prefix)
}
func __StringTrimSuffix(operand string, suffix string) string {
	return strings.TrimSuffix(operand, suffix)
}

func __StringHasPrefix(str string, prefix string) rt.EnumValue {
	return rt.ToBool(strings.HasPrefix(str, prefix))
}
func __StringHasSuffix(str string, suffix string) rt.EnumValue {
	return rt.ToBool(strings.HasSuffix(str, suffix))
}
func __StringContains(operand string, sub string) rt.EnumValue {
	return rt.ToBool(StringHasSubstring(operand, sub))
}


